package com.loklok.code.confuse.util;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.loklok.code.confuse.dto.GenerateCodeDto;
import com.loklok.code.confuse.model.ClassMetaData;
import com.loklok.code.confuse.model.CodePathConfig;
import com.loklok.code.confuse.ui.OverwriteFileDialog;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;


public class LocalFileUtils {

    // 全局状态，记录用户是否选择“应用于所有文件”
    private static final Map<Project, String> applyToAllMap = new ConcurrentHashMap<>();

    public static PsiDirectory getChildDirNotExistCreate(Project project, VirtualFile root, String childPackage) {
        return ApplicationManager.getApplication().runWriteAction((Computable<PsiDirectory>) () -> {
            try {
                String[] childs = childPackage.split("\\.");
                VirtualFile childFile = root;
                for (String child : childs) {
                    VirtualFile existChildFile = childFile.findChild(child);
                    if (existChildFile != null) {
                        childFile = existChildFile;
                    } else {
                        childFile = childFile.createChildDirectory(null, child); // ✅ 写操作现在在 write action 中
                    }
                }
                return PsiManager.getInstance(project).findDirectory(childFile);
            } catch (Exception e) {
                throw new RuntimeException("创建目录失败: " + childPackage, e);
            }
        });
    }


    public static PsiDirectory getChildDirFromAbsolutePath(Project project, String fullPath) {
        return ApplicationManager.getApplication().runReadAction((Computable<PsiDirectory>) () -> {
            try {
                Path path = Paths.get(fullPath);
                if (!Files.exists(path)) {
                    Files.createDirectories(path); // 创建缺失的目录
                }
                VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(path.toFile());
                if (vFile == null || !vFile.isDirectory()) {
                    throw new RuntimeException("无法找到或创建目录: " + fullPath);
                }
                return PsiManager.getInstance(project).findDirectory(vFile);
            } catch (Exception e) {
                throw new RuntimeException("创建目录失败: " + fullPath, e);
            }
        });
    }


    /**
     * 自动识别路径类型并创建 PSI 目录：
     * - 如果是 Java 包名（含 .）：调用 getChildDirNotExistCreate
     * - 如果是绝对路径（含 / 或 \）：调用 getChildDirFromAbsolutePath
     */
    public static PsiDirectory getOrCreateDirectory(Project project, VirtualFile root, String path) {
        if (path == null || path.isEmpty()) {
            return null;
        }

        if (path.contains("/") || path.contains("\\")) {
            return getChildDirFromAbsolutePath(project, path);
        } else {
            PsiDirectory dir = getChildDirNotExistCreate(project, root, path);
            if (dir != null) {
                return dir;
            } else {
                // 如果目录不存在，用 WriteAction 创建
                return createMissingDirectories(project, root, path);
            }
        }
    }

    public static PsiDirectory createMissingDirectories(Project project, VirtualFile root, String childPackage) {
        String[] parts = childPackage.split("\\.");

        VirtualFile current = root;
        for (String part : parts) {
            VirtualFile next = current.findChild(part);
            if (next == null) {
                // 需要创建新目录
                final VirtualFile[] newDir = new VirtualFile[1];
                VirtualFile finalCurrent = current;
                WriteCommandAction.runWriteCommandAction(project, () -> {
                    try {
                        newDir[0] = finalCurrent.createChildDirectory(null, part);
                    } catch (Exception e) {
                        throw new RuntimeException("创建目录失败: " + part, e);
                    }
                });
                current = newDir[0];
            } else {
                current = next;
            }
        }

        return PsiManager.getInstance(project).findDirectory(current);
    }

    public static void writeFile(AnActionEvent anActionEvent, String fullPck, String className, String generateCode, FileType fileType) {
        Project project = anActionEvent.getProject();
        VirtualFile thisVirtualFile = anActionEvent.getData(LangDataKeys.VIRTUAL_FILE);
        if (thisVirtualFile == null) {
            Messages.showErrorDialog("无法获取当前文件", "错误");
            return;
        }

        VirtualFile contentRoot = ProjectRootManager.getInstance(project).getFileIndex().getContentRootForFile(thisVirtualFile);
        if (contentRoot == null) {
            Messages.showErrorDialog("项目内容根目录为空", "错误");
            return;
        }

        PsiDirectory directory = getOrCreateDirectory(project, contentRoot, fullPck);
        if (directory == null) {
            Messages.showErrorDialog("目标目录创建失败: " + fullPck, "错误");
            return;
        }

        PsiFile existingFile = directory.findFile(className);

        // 如果已有文件存在，先处理是否覆盖
        AtomicBoolean overwrite = new AtomicBoolean(false);
        AtomicBoolean shouldProceed = new AtomicBoolean(true);

        if (existingFile != null) {
            if (!handleExistingFile(project, className, overwrite, shouldProceed)) {
                return;
            }
            if (!shouldProceed.get()) {
                return;
            }
        }

        // 真正执行创建文件的操作
        WriteCommandAction.runWriteCommandAction(project, () -> {
            try {
                PsiFile generateEntityFile = PsiFileFactory.getInstance(project)
                        .createFileFromText(className, fileType, generateCode);

                if (generateEntityFile == null) {
                    Messages.showErrorDialog("PSI 文件创建失败，请检查内容或 FileType", "错误");
                    return;
                }

                CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
                codeStyleManager.reformat(generateEntityFile);

                directory.add(generateEntityFile);

                // 主动刷新，确保文件写入磁盘
                directory.getVirtualFile().refresh(false, false);

                // 打印路径用于调试
                System.out.println("generate file: " + directory.getVirtualFile().getPath() + "/" + className);

            } catch (Exception ex) {
                System.out.println(ex);
                ApplicationManager.getApplication().invokeLater(() ->
                        Messages.showMessageDialog(ex.getMessage(), "Error", Messages.getErrorIcon())
                );
            }
        });
    }

    private static boolean handleExistingFile(Project project, String className, AtomicBoolean overwrite, AtomicBoolean shouldProceed) {
        // 检查是否已缓存用户选择
        if (applyToAllMap.containsKey(project)) {
            String choose = applyToAllMap.get(project);
            overwrite.set("Y".equalsIgnoreCase(choose));
            return true;
        }

        AtomicBoolean applyToAll = new AtomicBoolean(false);

        // 弹出对话框，只执行一次
        ApplicationManager.getApplication().invokeAndWait(() -> {
            OverwriteFileDialog dialog = new OverwriteFileDialog(className);
            dialog.show();
            if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
                overwrite.set(true);
                applyToAll.set(dialog.isApplyToAll());
            } else {
                applyToAll.set(dialog.isApplyToAll());
                shouldProceed.set(false);
            }
        }, ModalityState.defaultModalityState());

        if (!shouldProceed.get()) {
            return false;
        }

        if (applyToAll.get()) {
            applyToAllMap.put(project, overwrite.get() ? "Y" : "N");
        }

        return true;
    }



    public static void resetApplyToAll(Project project) {
        applyToAllMap.remove(project);
    }

    public static String fileReadString(String readPath){
        try {
            // 文件路径
            Path filePath = Path.of(readPath);
            // 读取文件为字符串
            String content = Files.readString(filePath, StandardCharsets.UTF_8);
            return content;
            // 输出文件内容
        } catch (Exception e) {

        }
        return null;
    }

    private static void doWriteMethod(GenerateCodeDto generateCodeDto, AnActionEvent anActionEvent, Project project) {
        Editor editor = anActionEvent.getData(CommonDataKeys.EDITOR);

        Document document = editor.getDocument();
        Caret currentCaret = editor.getCaretModel().getPrimaryCaret();
        // 插入代码到光标位置
        int offset = currentCaret.getOffset();

        try {
            document.insertString(offset, generateCodeDto.getGenerateCode());

            PsiFile psiFile = anActionEvent.getData(CommonDataKeys.PSI_FILE);
            if (psiFile != null) {
                PsiDocumentManager.getInstance(project).commitDocument(document);
                CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
                codeStyleManager.reformatText(psiFile, offset, offset + generateCodeDto.getGenerateCode().length());
            }
        } catch (Exception ex) {
            System.out.println(ex);
            ApplicationManager.getApplication().invokeLater(() ->
                    Messages.showMessageDialog(ex.getMessage(), "Error", Messages.getErrorIcon())
            );
        }
    }

    public static boolean isFileExist(Project project, CodePathConfig config, ClassMetaData item, String inputPckName) {
        String name = StringUtils.isNotEmpty(item.getClassName()) && item.getClassName().contains(".java") ? item.getClassName() : item.getClassName() + ".java";
        String relativePath = CommonUtil.pathJoint(config.getObtainRelativePath(item), inputPckName, CommonUtil.pckToClassPrefix(inputPckName) + name);

        // 获取项目基路径
        VirtualFile baseDir = project.getBaseDir();
        if (baseDir != null) {
            Path basePath = Paths.get(baseDir.getPath());
            Path absolutePath = basePath.resolve(relativePath);
            return Files.exists(absolutePath);
        }

        return false;
    }


    /**
     * 向指定的Java类中添加新方法
     *
     * @param project  当前项目
     * @param psiClass 目标类
     * @param methodText 要添加的方法的文本表示
     */
    /**
     * 向指定的Java类中添加新方法
     *
     * @param project  当前项目
     * @param psiClass 目标类
     * @param methodText 要添加的方法的文本表示
     */
    /**
     * 向指定的Java类中添加新方法
     *
     * @param project  当前项目
     * @param psiClass 目标类
     * @param methodText 要添加的方法的文本表示
     */
    public static void addMethodToClass(Project project, PsiClass psiClass, String methodText) {
        if (psiClass == null || StringUtils.isBlank(methodText)) {
            System.err.println("psiClass is null or methodText is blank");
            return;
        }

        WriteCommandAction.runWriteCommandAction(project, () -> {
            try {
                PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);

                // 解析并添加方法
                List<String> methods = extractMethods(methodText);

                for (String method : methods) {
                    String cleanedMethodText = validateAndCleanMethodText(method);
                    if (StringUtils.isNotBlank(cleanedMethodText)) {
                        // 修复：在创建方法前清理文本中的非法字符
                        String sanitizedMethodText = sanitizeMethodText(cleanedMethodText);
                        if (StringUtils.isNotBlank(sanitizedMethodText)) {
                            PsiMethod newMethod = factory.createMethodFromText(sanitizedMethodText, psiClass);
                            psiClass.add(newMethod);
                        }
                    }
                }

                // 自动格式化整个类
                CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
                codeStyleManager.reformat(psiClass);

            } catch (Exception e) {
                System.err.println("Error creating method from text: " + e.getMessage());
                System.err.println("Original method text: " + methodText);
                e.printStackTrace();
            }
        });
    }

    /**
     * 清理方法文本中的非法字符
     * @param methodText 原始方法文本
     * @return 清理后的方法文本
     */
    private static String sanitizeMethodText(String methodText) {
        if (methodText == null) {
            return null;
        }

        // 移除控制字符，但保留常见的空白字符
        StringBuilder sanitized = new StringBuilder();
        for (int i = 0; i < methodText.length(); i++) {
            char c = methodText.charAt(i);
            // 保留有效的字符范围
            if (c == '\n' || c == '\r' || c == '\t' || (c >= 32 && c <= 126) || c > 127) {
                sanitized.append(c);
            }
        }

        return sanitized.toString().trim();
    }



    /**
     * 从文本中提取单个方法
     */
    private static List<String> extractMethods(String methodText) {
        List<String> methods = new ArrayList<>();

        if (StringUtils.isBlank(methodText)) {
            return methods;
        }

        // 按双换行符分割（假设方法之间用空行分隔）
        String[] parts = methodText.split("\\n\\s*\\n");

        for (String part : parts) {
            String trimmedPart = part.trim();
            if (StringUtils.isNotBlank(trimmedPart)) {
                methods.add(trimmedPart);
            }
        }

        // 如果没有分割出内容或者分割结果不合理，回退到原始文本
        if (methods.isEmpty() || methods.size() > 30) { // 假设正常情况下方法数量有限
            methods.clear();
            methods.add(methodText.trim());
        }

        return methods;
    }

    /**
     * 验证并清理方法文本
     */
    private static String validateAndCleanMethodText(String methodText) {
        if (StringUtils.isBlank(methodText)) {
            return null;
        }

        // 去除首尾空白
        String cleaned = methodText.trim();

        // 确保方法文本不为空
        if (cleaned.isEmpty()) {
            return null;
        }

        return cleaned;
    }

    public static class PromptTemplate {

        private static Map<String, String> fileContentMap = new HashMap<>();

        public static void readAllFilesRecursively(String folderPath) {
            Path existsFolderPath = Paths.get(folderPath);
            if (!Files.exists(existsFolderPath)) {
                return;
            }

            removeAllFiles();

            try (Stream<Path> paths = Files.walk(existsFolderPath)) {
                paths.forEach(path -> {
                    if (Files.isRegularFile(path)) {
                        String content = null;
                        try {
                            // 明确指定使用 UTF-8 编码
                            content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        fileContentMap.put(path.getFileName().toString(), content);
                    }
                });
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        public static String getFileContent(String fileName) {
            return fileContentMap.get(fileName);
        }


        private static void removeAllFiles() {
            fileContentMap.clear();
        }
    }


}
